package com.xh.common.thread.ext.limit;

import android.util.Pair;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantLock;

public class LimitWorker<T> {

    private final LimitThreadPoolExecutor<T> mExecutor;

    // 限制任务最大同时执行数
    private final int mLimitSize;

    // 当前的正在执行的任务数
    private volatile int mActive = 0;

    // 当前的任务队列
    private Queue<Pair<Schedule, T>> mCommands;

    // 这个锁同时锁住队列的操作和当前任务数
    private ReentrantLock mLock;

    public LimitWorker(LimitThreadPoolExecutor<T> executor, int size) {
        mExecutor = executor;
        mLimitSize = size;
        mLock = new ReentrantLock();
        mCommands = new ArrayDeque<>(5);
    }

    public int getLimitSize() {
        return mLimitSize;
    }

    public synchronized List<Runnable> shutdownNow() {
        List<Runnable> runnable = new ArrayList<>();

        mLock.lock();
        try {
            for (Pair<Schedule, T> pair : mCommands) {
                runnable.add(pair.first.mRunnable);
            }
        } finally {
            mLock.unlock();
        }

        return runnable;
    }

    public void execute(final Runnable command, T t) {
        Runnable target = null;
        T param = null;

        mLock.lock();
        try {

            // 如果没有达到运行数量的限制，就新启任务
            if (mActive < mLimitSize) {
                mActive++;

                // 执行队头任务，如果没有就执行当前的任务
                Pair<Schedule, T> first = mCommands.poll();

                if (first != null) {
                    mCommands.offer(new Pair<>(new Schedule(command), t));

                    target = first.first;
                    param = first.second;
                } else {
                    target = new Schedule(command);
                    param = t;
                }
            } else {
                mCommands.offer(new Pair<>(new Schedule(command), t));
            }
        } finally {
            mLock.unlock();
        }

        // 放到锁的外面执行，这个代码保证同步好像没有什么意义
        if (target != null) {
            mExecutor.executeInternal(target, param);
        }
    }

    private void scheduleNext() {
        Pair<Schedule, T> command = null;

        mLock.lock();
        try {
            // 上一个执行完成
            if (mActive <= 0) {
                throw new IllegalAccessError();
            }
            mActive--;

            // 继续执行下面的任务
            if (mActive < mLimitSize) {
                command = mCommands.poll();
                if (command != null) {
                    mActive++;
                }
            }
        } finally {
            mLock.unlock();
        }

        if (command != null) {
            mExecutor.executeInternal(command.first, command.second);
        }
    }

    private class Schedule implements Runnable {

        private Runnable mRunnable;

        public Schedule(Runnable runnable) {
            mRunnable = runnable;
        }

        @Override
        public void run() {
            try {
                mRunnable.run();
            } finally {
                scheduleNext();
            }
        }
    }
}
